using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Citrix.Cmp;

namespace SkinningSample.ViewModel
{
    /// <summary>
    /// The navigation implementation. It provides simple forward/back navigation using a
    /// navigation history.
    /// </summary>
    internal class Navigation
    {
        /// <summary>
        /// The currently visible page.
        /// </summary>
        private IPage _currentPage;

        /// <summary>
        /// Holds the user's navigation history. This queue is used to implement back
        /// navigation.
        /// </summary>
        private List<IPage> _navigationHistory;

        /// <summary>
        /// A reference back to the root v-m object.
        /// </summary>
        private readonly ViewModelManager _vmRoot;

        /// <summary>
        /// Fired whenever the current page is changed.
        /// </summary>
        public event EventHandler<PageChangedArgs> PageChanged;

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="vmManager">The root view-model object.</param>
        /// <param name="startingPage">The first page to display at startup.</param>
        /// <exception cref="ArgumentNullException">Thrown if any argument is null.</exception>
        internal Navigation(ViewModelManager vmManager, IPage startingPage)
        {
            if (null == vmManager)
            {
                throw new ArgumentNullException("vmManager");
            }
            if (null == startingPage)
            {
                throw new ArgumentNullException("startingPage");
            }

            _vmRoot = vmManager;
            _currentPage = startingPage;
            _navigationHistory = new List<IPage>();
            _navigationHistory.Add(startingPage);
        }

        /// <summary>
        /// The current page to display to the user.
        /// </summary>
        public IPage CurrentPage
        {
            get { return _currentPage; }
        }

        /// <summary>
        /// Performs a back navigation operation to the previous page in the navigation history.
        /// </summary>
        public void NavigateBack()
        {
            if (_navigationHistory.Count > 1)
            {
                var oldPage = this._currentPage;
                _navigationHistory.RemoveAt(_navigationHistory.Count - 1);
                _currentPage = _navigationHistory.Last();

                oldPage.NotifyActiveChanged(false);
                _currentPage.NotifyActiveChanged(true);
                UpdateBackButtonRedirection();

                this.PageChanged.Raise(this, new PageChangedArgs { NewPage = _currentPage, OldPage = oldPage });
            }
        }

        /// <summary>
        /// Requests that the specified page be displayed.
        /// </summary>
        /// <param name="page">The page to display.</param>
        public void NavigateForward(IPage page)
        {
            if (null != page && !object.ReferenceEquals(page, _currentPage))
            {
                var oldPage = this._currentPage;
                _currentPage = page;

                _navigationHistory.Add(page);
                oldPage.NotifyActiveChanged(false);
                _currentPage.NotifyActiveChanged(true);
                UpdateBackButtonRedirection();

                this.PageChanged.Raise(this, new PageChangedArgs { NewPage = page, OldPage = oldPage });
            }
        }

        /// <summary>
        /// Handler for a physical button press on the device.
        /// </summary>
        /// <param name="buttonId">The button that was pressed.</param>
        public void NotifyButtonPressed(ButtonId buttonId)
        {
            if (buttonId == ButtonId.Back)
            {
                if (_currentPage != null)
                {
                    _currentPage.NotifyBackButtonPressed();
                }
            }
        }

        /// <summary>
        /// Updates the state of the back button redirection based on the state of the
        /// navigation history. When we have more than one item in the history we redirect the
        /// back button to the Citrix server so we can handle it to perform back navigation in
        /// our application. When there is only one item in the history we cannot perform back
        /// navigation, so we disable back button direction. This makes the Citrix receiver
        /// use the default behavior for the back button which is to disconnect the HDX
        /// connection.
        /// </summary>
        private void UpdateBackButtonRedirection()
        {
            ButtonTarget target = ButtonTarget.ClientDevice;
            if (_navigationHistory.Count > 1)
            {
                target = ButtonTarget.Server;
            }
            _vmRoot.App.CmpWpfApi.Input.BeginSetButtonTarget(ButtonId.Back, target, null);
        }
    }
}
